package cn.mesmile.shiro.config;

import cn.mesmile.shiro.module.jwt.JwtCredentialsMatcher;
import cn.mesmile.shiro.module.jwt.JwtFilter;
import cn.mesmile.shiro.module.jwt.JwtRealm;
import cn.mesmile.shiro.module.jwt.MultiRealmAuthenticator;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zb
 * @date 2021/2/7 17:58
 * @Description:
 */
@Configuration
public class MyShiroConfig {

    /**
     * 配置流程和思路
     *
     * shiroFilterFactoryBean-》
     *      SecurityManager-》
     *              CustomSessionManager
     *              CustomRealm-》hashedCredentialsMatcher
     * SessionManager
     *
     *  DefaultSessionManager： 默认实现，常用于javase
     *  ServletContainerSessionManager: web环境
     *  DefaultWebSessionManager：常用于自定义实现
     */


    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){


        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        shiroFilterFactoryBean.setSecurityManager(securityManager);


        // 需要登录的接口，如果访问某个接口，【需要登录却没登录】就会---->调用此接口（如果不是前后端分离，则跳转页面）
        shiroFilterFactoryBean.setLoginUrl("/pub/need_login");

        // 登录成功，跳转Url,如果前后端分离，则没这个调用
        shiroFilterFactoryBean.setSuccessUrl("/");

        // 没有权限，未授权就会调用此方法，先验证登录--》 再验证是否有权限
        shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit");

        // 自定义过滤器
        Map<String, Filter> filtersMap = new LinkedHashMap<>();
        filtersMap.put("roleOrFilter",new CustomRolesOrAuthorizationFilter());
//        filtersMap.put("forceLogout", new CustomForceLogoutFilter());
        filtersMap.put("jwtFilter", new JwtFilter());

        // shiroFilterFactoryBean绑定自定义过滤器
        shiroFilterFactoryBean.setFilters(filtersMap);

        // 默认过滤器 【org.apache.shiro.web.filter.mgt  DefaultFilter】
        // 拦截器路径，部门路径无法进行拦截，时有时无；是因为同学使用的hashmap,无序的，应该改为LinkHashMap
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 退出过滤器
        filterChainDefinitionMap.put("/logout", "logout");
        // 匿名可以访问，也就是游客模式
        filterChainDefinitionMap.put("/pub/**", "anon");



        // 需要登录才可以访问
        filterChainDefinitionMap.put("/authc/**", "authc");
        // 需要管理员 角色才可以访问                此处配置意思为：用户同时拥有 admin和root 权限才可以访问
        filterChainDefinitionMap.put("/admin/**", "roleOrFilter[admin,root]");
        // 有编辑权限才可以访问
        filterChainDefinitionMap.put("/video/update", "perms[video_update]");

        // 自定义过滤器
//        filterChainDefinitionMap.put("/admin/**","forceLogout,roleOrFilter[admin,root]");


        // 除了需要忽略的就进入到强制退出检测
//        filterChainDefinitionMap.put("/**/**","forceLogout");

        // 过滤链是顺序执行，从上而下，一般讲 /** 放到最下面

        // authc : url定义必须通过认证才可以访问
        // anon : url可以匿名访问
        filterChainDefinitionMap.put("/**", "jwtFilter,authc");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }


//    @Bean
//    public SecurityManager securityManager(){
//        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//
//        // 如果不是前后端分离，就不用设置 sessionManager
//        securityManager.setSessionManager(sessionManager());
//
//        // redis 整合shiro
//        securityManager.setCacheManager(redisCacheManager());
//
//        // 推荐放到最后，不然某些情况下不生效
//        securityManager.setRealm(customRealm());
//
//        return securityManager;
//    }

    /**
     * 配置 SecurityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 1.Authenticator
        securityManager.setAuthenticator(authenticator());

        // 2.Realm
        List<Realm> realms = new ArrayList<Realm>(16);
        realms.add(jwtRealm());
        realms.add(customRealm());
        securityManager.setRealms(realms);

        // 3.关闭shiro自带的session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
        securityManager.setSubjectDAO(subjectDAO);

        return securityManager;
    }

    /**
     * JwtRealm 配置，需实现 Realm 接口
     */
    @Bean
    JwtRealm jwtRealm() {
        JwtRealm jwtRealm = new JwtRealm();
        // 设置加密算法
        CredentialsMatcher credentialsMatcher = new JwtCredentialsMatcher();
        // 设置加密算法
        jwtRealm.setCredentialsMatcher(credentialsMatcher);
        return jwtRealm;
    }


    /**
     * 自定义 shiro 数据源
     * @return
     */
    @Bean
    public CustomRealm customRealm(){
        CustomRealm customRealm = new CustomRealm();

        // 使用加密方式验证 登录
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());

        return customRealm;
    }


    /**
     * 配置 ModularRealmAuthenticator
     */
    @Bean
    public ModularRealmAuthenticator authenticator() {
        ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator();
        // 设置多 Realm的认证策略，默认 AtLeastOneSuccessfulStrategy
        AuthenticationStrategy strategy = new FirstSuccessfulStrategy();
        authenticator.setAuthenticationStrategy(strategy);
        return authenticator;
    }

    /**
     * 禁用session, 不保存用户登录状态。保证每次请求都重新认证
     */
    @Bean
    protected SessionStorageEvaluator sessionStorageEvaluator() {
        DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        return sessionStorageEvaluator;
    }



//    @Bean
//    public SessionManager sessionManager(){
//        CustomSessionManager customSessionManager = new CustomSessionManager();
//
//        // 设置session过期时间，默认 30 分钟，单位是 毫秒
//        customSessionManager.setGlobalSessionTimeout(1800*1000);
//
//        // 使用 Redis 自定义sessionDao 来持久化session
//        customSessionManager.setSessionDAO(redisSessionDAO());
//
//        return customSessionManager;
//    }

    /**
     * 设置密码 加密相关
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        // 使用自定义 次数限制拦截
        RetryLimitHashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher();
        // 设置加密方式
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置hash次数
        hashedCredentialsMatcher.setHashIterations(2);

        return hashedCredentialsMatcher;
    }

    /**
     * redis 整合shiro
     * @return
     */
//    @Bean
//    public RedisManager getRedisManager(){
//        RedisManager redisManager = new RedisManager();
//        redisManager.setHost("81.69.43.78:6379");
//        redisManager.setPassword("root");
//
//        return redisManager;
//    }

//    @Bean
//    public RedisCacheManager redisCacheManager(){
//        RedisCacheManager redisCacheManager = new RedisCacheManager();
//        // 设置过期时间，默认过期时间为 30 分钟，单位秒
//        redisCacheManager.setExpire(1800);
//        redisCacheManager.setRedisManager(getRedisManager());
//        return redisCacheManager;
//    }

    /**
     * 自定义 Session 的持久化
     * @return
     */
//    @Bean
//    public RedisSessionDAO redisSessionDAO(){
//        // 实体类需要实现 Serializable 序列化接口
//        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
//        // 设置默认的超期时间，默认 30 分钟，单位秒
//        redisSessionDAO.setExpire(1800);
//
//        redisSessionDAO.setRedisManager(getRedisManager());
//
//        // 自定义sessionId 生成
//        redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator());
//
//        return redisSessionDAO;
//    }

    /**
     * 管理shiro一些bean的生命周期 即bean初始化 与销毁
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 加入注解的使用，不加入这个AOP注解不生效(shiro的注解 例如 @RequiresGuest)
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 作用: 用来扫描上下文寻找所有的Advistor(通知器), 将符合条件的Advisor应用到切入点的Bean中，
     * 需要在LifecycleBeanPostProcessor创建后才可以创建
     * @return
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }

}
